﻿using System;
using System.Text;
using System.Xml;
using System.Net;
using System.Net.Sockets;
using Protocolo.Mensajes;
using Protocolo.Enums;
using System.Linq;
using Protocolo.Mensajes.Request;
using Protocolo.Mensajes.Response;
using System.IO;
using Protocolo.Utilidades;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Threading;

namespace Cliente
{
    public class Controlador
    {
        private volatile bool _shouldStop;
        private static Controlador _instancia = null;
        private static DescubrirServidorResponse responseDescubrimiento = null;
        private static Thread escucharRespuestaDescubrimiento = null;

        public static Controlador Instancia
        {
            get
            {
                if (_instancia == null)
                {
                    _instancia = new Controlador();
                }
                return _instancia;
            }
        }

        protected Controlador()
        {
        }

        public void EnviarArchivo(string pathArchivo)
        {
            try
            {
                //Obtengo el servidor
                var responseServidor = DescubrirServidor();

                //Valido si algun servidor respondio
                if (responseServidor == null)
                {
                    MessageBox.Show("Error conectandose a servidor: ningun servidor respondio el pedido de descubrimiento", "Subir Archivo", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                //Obtengo el archivo
                byte[] bytesArchivo = Archivos.LeerArchivo(pathArchivo);

                var request = new SubirArchivoRequest();
                request.NombreArchivo = Path.GetFileName(pathArchivo);
                request.MD5 = Checksum.ObtenerChecksum(bytesArchivo);
                request.LargoArchivo = bytesArchivo.LongLength;
                request.Archivo = bytesArchivo;

                //Envio el archivo y obtengo respuesta
                var response = request.EnviarArchivo(responseServidor.IPServidor, responseServidor.PuertoServidor, null);

                if (response.Resultado == ResultadoTransferenciaArchivoEnum.OK)
                    MessageBox.Show("Archivo subido con exito", "Subir Archivo", MessageBoxButtons.OK, MessageBoxIcon.Information);
                else if (response.Resultado == ResultadoTransferenciaArchivoEnum.ArchivoYaExiste)
                {
                    MessageBox.Show("Error: " + ObtenerMensajeEnum.ResultadoTransferenciaArchivo(ResultadoTransferenciaArchivoEnum.ArchivoYaExiste), "Subir Archivo", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else
                {
                    //Espero 5 segundo y reintento enviar el archivo
                    Thread.Sleep(5000);
                    EnviarArchivo(pathArchivo);
                }
            }
            catch
            {
                //Reintento enviar el archivo
                Thread.Sleep(5000);
                EnviarArchivo(pathArchivo);
            }
        }

        public void DescargarArchivo(string nombreArchivo)
        {
            try
            {
                //Obtengo el servidor
                var responseServidor = DescubrirServidor();

                //Valido si algun servidor respondio
                if (responseServidor == null)
                {
                    MessageBox.Show("Error conectandose a servidor: ningun servidor respondio el pedido de descubrimiento", "Descargar Archivo", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                //Veo si el archivo ya existe
                string path = string.Format("{0}\\{1}", DatosCliente.Instancia.FileRepository, nombreArchivo);

                var request = new DescargarArchivoRequest();
                request.NombreArchivo = nombreArchivo;
                request.ByteDesde = Archivos.ExisteArchivo(path) ? Archivos.LeerArchivo(path).LongLength : 0;

                //Envio pedido y obtengo respuesta
                var response = request.DescargarArchivo(responseServidor.IPServidor, responseServidor.PuertoServidor, DatosCliente.Instancia.FileRepository, null);

                if (response.Resultado == ResultadoTransferenciaArchivoEnum.OK)
                    MessageBox.Show("Archivo descargado con exito", "Descargar Archivo", MessageBoxButtons.OK, MessageBoxIcon.Information);
                else if (response.Resultado == ResultadoTransferenciaArchivoEnum.ArchivoNoExiste)
                    MessageBox.Show("Error: " + ObtenerMensajeEnum.ResultadoTransferenciaArchivo(ResultadoTransferenciaArchivoEnum.ArchivoNoExiste), "Descargar Archivo", MessageBoxButtons.OK, MessageBoxIcon.Error);
                else
                {
                    //Reintento descargar el archivo
                    DescargarArchivo(nombreArchivo);
                }
            }
            catch
            {
                //Reintento descargar el archivo
                DescargarArchivo(nombreArchivo);
            }
        }

        public DescubrirServidorResponse DescubrirServidor()
        {          
            try
            {
                responseDescubrimiento = null;

                //Envio pedido de descubrimiento
                var request = new DescubrirServidorRequest();
                request.IPCliente = DatosCliente.Instancia.IPCliente;
                request.EnviarBroadcast(DatosCliente.Instancia.PuertoUDP);

                return escucharRespuestaDescubrimiento.Join(DatosCliente.Instancia.TimeoutDescubrimiento) ? responseDescubrimiento : null;
            }
            catch
            {
                return null;
            }
          
        }

        public void InciarEscucharDescubrimiento()
        {
            ThreadStart starterAtenderDescubrimiento = delegate { EscucharRespuestaDescubrimiento(); };
            escucharRespuestaDescubrimiento = new Thread(starterAtenderDescubrimiento);
            escucharRespuestaDescubrimiento.Start();
        }

        public void DetenerEscucharDescubrimiento()
        {
            escucharRespuestaDescubrimiento.Abort();
        }

        private void EscucharRespuestaDescubrimiento()
        {
            try
            {
                //Recibo respuesta del servidor
                var response = new DescubrirServidorResponse();
                response.RecibirBroadcast(DatosCliente.Instancia.PuertoUDP);

                responseDescubrimiento = response;
            }
            catch
            {
                responseDescubrimiento = null;
            }
            finally
            {
                if(!_shouldStop)
                    _instancia.InciarEscucharDescubrimiento();
            }
        }

        public void RequestStop()
        {
            _shouldStop = true;
        }
       
    }
}
